package controller;

import java.awt.Color;
import java.awt.GradientPaint;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.math.BigDecimal;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Timer;
import java.util.TimerTask;

import model.BinomialModel;

import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.plot.CategoryPlot;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.renderer.category.BarRenderer;
import org.jfree.data.category.CategoryDataset;

import view.BinomialView;

public class BinomialController {
	
	private BinomialView binView;
	private BinomialModel binModel;
	
	private JFreeChart chart;
    private ChartPanel chartPanel;
	
    private int counter = 0;
    
	public BinomialController(BinomialView binView, BinomialModel binModel) {
		this.binView = binView;
		this.binModel = binModel;
		
		this.binView.addComputeListener(new ComputeListener());
		this.binView.addNListener(new NListener());
		this.binView.addPListener(new PListener());
	}
	
	
	class ComputeListener implements ActionListener {
		@Override
		public void actionPerformed(ActionEvent e) {
			int x1;
			int n;
			double P;
			int x2;
			String message = "";

			if(checkInputsAreValid()){

				binView.txtSuccess1.setBackground(Color.WHITE);
				binView.txtSuccess2.setBackground(Color.WHITE);
				binView.txtTrials.setBackground(Color.WHITE);
				binView.txtProbability.setBackground(Color.WHITE);
				
				x1 = Integer.parseInt(binView.getSuccess1());
				x2 = Integer.parseInt(binView.getSuccess2());
				n = Integer.parseInt(binView.getTrials());
				P = Double.parseDouble(binView.getProbability());
				
				binView.getTableModel().setRowCount(0);
				binModel.clearMap();
				binModel.clearDataSet();
			
				if(!(P >= 0 && P <= 1) || x1 > x2 || n < 0){		
					// INPUT LOGIC CHECKER
					if(x1 > x2){
						message += "Number of Successes is not in ascending order.\n";
						binView.txtSuccess1.setBackground(Color.PINK);
						binView.txtSuccess2.setBackground(Color.PINK);
					}
					if(n < 0){
						message += "Number of trials is invalid. Allowable values are only zero and up.\n";
						binView.txtTrials.setBackground(Color.PINK);
					}
					if(!(P >= 0 && P <= 1)){
						message += "Probability of success(P) should be greater than or equal to zero AND less than or equal to one.";
						binView.txtProbability.setBackground(Color.PINK);
					}
					
					binView.displayErrorMessage(message);
				}
				else if(x1 == x2) {
					binModel.computeBinomialProbability(x1, n, P);
					binView.setBinomialAnswer(binModel.getBinomialProbability());
					binView.getTableModel().addRow(new Object[]{x1, BigDecimal.valueOf(binModel.getBinomialProbability())});
				}
				else {
					binModel.computeBinomialProbability(x1, x2, n, P);
					binView.setBinomialAnswer(binModel.getBinomialProbability());
					chart = createChart(binModel.getDataSet());
					chartPanel = new ChartPanel(chart);
					
					Iterator i = binModel.getMap().entrySet().iterator();
					while(i.hasNext()) {
						Map.Entry<Integer, Double> my = (Entry<Integer, Double>) i.next();
						binView.getTableModel().addRow(new Object[]{my.getKey(), BigDecimal.valueOf(my.getValue())});
						}
					binView.addChartPanel(chartPanel);
				}
			}
			else
				binView.displayErrorMessage("Please complete the text fields with proper values.");
		}
	}
	class NListener implements ActionListener {
		@Override
		public void actionPerformed(ActionEvent e) {
			if(!(binView.getNSimulation().equals("") || Integer.parseInt(binView.getNSimulation()) < 1 )) {
				final Timer timer = new Timer(); 
				timer.schedule( new TimerTask() {
					public void run() {
					   	int n = Integer.parseInt(binView.getTrials());
						n += 1;
						reCompute();
						binView.setTrials(n);
						counter++;
					
						if(counter == Integer.parseInt(binView.getNSimulation())) {
							counter = 0;
							timer.cancel();
						}
								
					 } 
				}, 1000, 1000);			
			}
			else
				binView.displayErrorMessage("Please complete with proper values.");
			
		}
	}
	
	class PListener implements ActionListener {
		@Override
		public void actionPerformed(ActionEvent e) {
			
			if(!(binView.getPSimulation().equals("") || Integer.parseInt(binView.getPSimulation()) < 1 || 
			(Double.parseDouble(binView.getPSimulation()) * 0.01 + Double.parseDouble(binView.getProbability()) > 1.0))) {
				final Timer timer = new Timer();
				timer.schedule( new TimerTask() {
				    public void run() {
				    	double p = Double.parseDouble(binView.getProbability());
						p += 0.01;
						reCompute();
						binView.setProbability(p);
						counter++;
						
						if(counter == Integer.parseInt(binView.getPSimulation())) {
							counter = 0;
							timer.cancel();
						}
							
				    }
				 }, 1000, 1000);
			}
			else 
				binView.displayErrorMessage("Please complete with proper values.");
			
			
		}
		
	}
	
	private JFreeChart createChart(final CategoryDataset dataset) {
        JFreeChart barChart = ChartFactory.createBarChart("Graph for the Binomial Probability Distribution","X","P(X)",dataset,PlotOrientation.VERTICAL,true, true,false);
        barChart.setBackgroundPaint(new Color(0, 0, 0, 0));

        CategoryPlot plot = barChart.getCategoryPlot();
        plot.setBackgroundPaint(Color.lightGray);
        plot.getDomainAxis().setCategoryMargin(0.0);
        
        NumberAxis rangeAxis = (NumberAxis) plot.getRangeAxis();
        rangeAxis.setStandardTickUnits(NumberAxis.createStandardTickUnits());

        BarRenderer renderer = (BarRenderer) plot.getRenderer();
        renderer.setDrawBarOutline(false);
        final GradientPaint gp0 = new GradientPaint(
            0.0f, 0.0f, Color.blue, 
            0.0f, 0.0f, Color.lightGray
        );
        renderer.setSeriesPaint(0, gp0);
    
        return barChart;   
    }
	
	public void reCompute() {
		int x1;
		int n;
		double P;
		int x2;
		
		x1 = Integer.parseInt(binView.getSuccess1());
		x2 = Integer.parseInt(binView.getSuccess2());
		n = Integer.parseInt(binView.getTrials());
		P = Double.parseDouble(binView.getProbability());
		
		binView.getTableModel().setRowCount(0);
		binModel.clearMap();
		
		binModel.computeBinomialProbability(x1, x2, n, P);
		binView.setBinomialAnswer(binModel.getBinomialProbability());
		chart = createChart(binModel.getDataSet());

		Iterator i = binModel.getMap().entrySet().iterator();
		while(i.hasNext()) {
			Map.Entry<Integer, Double> my = (Entry<Integer, Double>) i.next();
			binView.getTableModel().addRow(new Object[]{my.getKey(), BigDecimal.valueOf(my.getValue())});
			}
	}

	
	
	public boolean checkInputsAreValid(){
		int x1;
		int n;
		double P;
		int x2;
		int ctr = 0;
		
		try{
			x1 = Integer.parseInt(binView.getSuccess1());
			binView.txtSuccess1.setBackground(Color.WHITE);
		} catch(NumberFormatException e1){
			binView.txtSuccess1.setBackground(Color.PINK);
			ctr++;
		}
		
		try{
			x2 = Integer.parseInt(binView.getSuccess2());
			binView.txtSuccess2.setBackground(Color.WHITE);
		} catch(NumberFormatException e1){
			binView.txtSuccess2.setBackground(Color.PINK);
			ctr++;
		}
		
		try{
			n = Integer.parseInt(binView.getTrials());
			binView.txtTrials.setBackground(Color.WHITE);
		} catch(NumberFormatException e1){
			binView.txtTrials.setBackground(Color.PINK);
			ctr++;
		}
		
		try{
			P = Double.parseDouble(binView.getProbability());
			binView.txtProbability.setBackground(Color.WHITE);
		} catch(Exception e1){
			binView.txtProbability.setBackground(Color.PINK);
			ctr++;
		}
		
		if(ctr == 0)
			return true;
		else
			return false;
	}
	
	
}
